import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class client {
	BufferedReader scriptfile;
	private static Logger log;
	String host;
	String bindName;
	Integer port=0;
	int queryNum = 0;
	QueryInterface brokerstub;
	
	private final String INCORRECT_PROTOCOL_ERR = "0x003";
	
	public client(String host, String bindName, BufferedReader scriptfile)
	{
		this.host = host;
		this.bindName = bindName; 
		this.scriptfile = scriptfile;
	}
	
	private void init() throws RemoteException, NotBoundException
	{
		Registry registry; 
		if(port != 0 )
			registry = LocateRegistry.getRegistry(host,port);
		else
			registry = LocateRegistry.getRegistry(host);
		
		brokerstub = (QueryInterface)registry.lookup(bindName);
		
	}
	
	float findmax(Map<Float, String> map)
	{
		float max = 0.0f;
		for(float val: map.keySet())
		{
			if(val > max)
				max=val;
		}
		return max;
	}

	String runStub(ProviderInterface provStub, List<String> cmdlist) throws RemoteException, IOException
	{
		String result="", var1 =null , var2 =null;
		
		if(cmdlist.size() > 2)
		{
			var1 = cmdlist.get(1);
			var2 = cmdlist.get(2);
		}
		
		if(cmdlist.get(0).equalsIgnoreCase("add_number"))
		{
			isPrefLength(cmdlist, 3);
			result = provStub.add_number(var1, var2);
		}
		else if(cmdlist.get(0).equalsIgnoreCase("subtract_number"))
		{
			isPrefLength(cmdlist, 3);
			result = provStub.subtract_number(var1, var2);
		}
		else if(cmdlist.get(0).equalsIgnoreCase("multiply_number"))
		{
			isPrefLength(cmdlist, 3);
			result = provStub.multiply_number(var1, var2);
		}
		else if(cmdlist.get(0).equalsIgnoreCase("divide_number"))
		{
			isPrefLength(cmdlist, 3);
			result= provStub.divide_number(var1, var2);;
		}
		else if(cmdlist.get(0).equalsIgnoreCase("date"))
		{
			isPrefLength(cmdlist, 2);
			if(cmdlist.size() > 1)
				result = provStub.date(cmdlist.get(1));
			else
				result = provStub.date("");
		}else if(cmdlist.get(0).equalsIgnoreCase("generate_random"))
		{
			isPrefLength(cmdlist, 3);
			result = provStub.generate_random(var1, var2);
		}
			
		return result;
	}
	
	private String queryandRun(List<String> cmdlist) throws IOException,NotBoundException,RemoteException
	{
		/*Get list of all services from Broker.*/
		queryNum++;
		String queryresponse = brokerstub.query(Integer.toString(queryNum), cmdlist.get(0), "*"); 
		
		//parse the queryresponse 
		String[] serviceList = queryresponse.split("\n");
		
		Map<Float, String> servicemap = new HashMap<Float, String>();
		
		if(serviceList.length == 1)
			return "Did not find service!";
		
		//start from 1 , since the first line is query-response.
		for(int i=1; i < serviceList.length; i++)
		{
			String[] service = serviceList[i].split(" ");
			float version;
			try{
				 version = Float.parseFloat(service[4]);
			}catch(Exception e) { continue; } //ignore bad values.
				
			servicemap.put(version, serviceList[i]);
		}
		
		//now get the highest version.
		float max = findmax(servicemap);
		
		String[] service = ((String)servicemap.get(max)).split(" ");
		
		//Now connect to that host and run corresponding query.
		Registry registry;
		registry = LocateRegistry.getRegistry(service[5]);
		String response= "";
		try{
			ProviderInterface provStub = (ProviderInterface)registry.lookup(service[3]);  
			response = runStub(provStub, cmdlist);
		}catch(Exception e)
		{
			response = "Failed to Connect to service provider!";
			log.info("Exception trying to connect to remote registry: " + e.getMessage());
		}
		
		return response;
	}
	
	public Integer getPort() {
		return port;
	}

	public void setPort(Integer port) {
		this.port = port;
	}

	public static List<String> sanitize(String[] inp )  
	{
		List<String> strList = new ArrayList<String>();
		
		for(String str: inp)
		{
			if(str.equals(""))
				continue;
			strList.add(str);
		}
		
		return strList;
	}
	
	private void isPrefLength(List<String> strList, int prefsize) throws IOException
	{
		if(strList.size() != prefsize)
		{
			log.error( "0x003 "+ prefsize + "!= String Length : " + strList.size() + " " + strList);
			/*error(INCORRECT_PROTOCOL_ERR); */
			throw new IOException("Incorrect Command");
		}
		
	}
	
	public void run() throws Exception
	{
			
		
		log.info("Connected to Broker.");
		
		while(true)
	    {
		try
		    {
			String servresponse="";	
			System.out.print("Command>");
			
			String strin = scriptfile.readLine();
			if(strin == null)
			    break;
			strin = strin.trim();
	    	
			if(strin.equalsIgnoreCase("END") || strin.equalsIgnoreCase("BYE") || strin.equalsIgnoreCase("EXIT"))
			    break;
	    	
			
			
			if(strin.length() == 0)
			    continue;
			
			if(strin.charAt(0) == '%')
			{
				continue;
			}
	    	
			List<String> cmdlist = sanitize(strin.split(" "));
			
			if(cmdlist.size() == 0)
			    continue;
			
			if(cmdlist.get(0).equalsIgnoreCase("SLEEP"))
			    {
				int sec = 0;
				try {
				    sec = Integer.parseInt(cmdlist.get(1));
				    Thread.sleep(sec * 1000);
				}catch( Exception e )
				    {
					log.error("Sleep Exception:" + e);
				    }
				continue;
			    }
			
			servresponse = queryandRun(cmdlist);
			
			System.out.println("Server Response:\n" + servresponse);
			
		    }catch (IOException ex)
		    {	 }

	    }/*End while*/

		/*Handle exit case here.*/
		
	}	
	
	public static void main(String args[])
	{
		log = Logger.getLogger();
		if (args.length < 2){
			System.out.println("Usage java client brokerIP bindname [scriptfile] [port]");
			System.exit(1);
		}
		
		try{
			BufferedReader reader;
			
			if (args.length >= 3)
				reader = new BufferedReader(new FileReader(args[2]));
			else reader = new BufferedReader( new InputStreamReader(System.in));
					
			client client = new client(args[0], args[1], reader);

			if(args.length >= 4)
				client.setPort(Integer.parseInt(args[4]));
			
			client.init();
			client.run();
			
		}catch(Exception ex)
		{
			log.error("Cought Exception : "+ex);
		}
		
		
	}

}
